package com.ejlchina.cashier.juhe;

import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ejlchina.cashier.CashNotifyData;
import com.ejlchina.cashier.CashReq;
import com.ejlchina.cashier.Cashier;
import com.ejlchina.cashier.Cashier.CashStarter;
import com.ejlchina.cashier.CashierException;
import com.ejlchina.cashier.CashierManager;
import com.ejlchina.cashier.MerAddExtReq;
import com.ejlchina.cashier.MerAddReq;
import com.ejlchina.cashier.MerEditRateReq;
import com.ejlchina.cashier.MerEditReq;
import com.ejlchina.cashier.MerNotifyData;
import com.ejlchina.cashier.MerRes;
import com.ejlchina.cashier.PayNotifyData;
import com.ejlchina.cashier.PayRes;
import com.ejlchina.cashier.PayType;
import com.ejlchina.cashier.Payer;
import com.ejlchina.cashier.QuickCashReq;
import com.ejlchina.cashier.Res;
import com.ejlchina.cashier.util.AESUtils;
import com.ejlchina.cashier.util.StrKit;



/**
 * 
 * 聚合收款管理器
 * 
 * @author Troy.Zhou
 *
 */
public class JuheCashierManager implements CashierManager {

	
	Logger log = LoggerFactory.getLogger(JuheCashierManager.class);
	
	
	final String MER_ADD = "merAdd";				// 进件
	final String MER_ADD_EXT = "merAddExt";			// 补充进件
	final String MER_EDIT = "merEdit";				// 修改进件
	final String MER_EDIT_RATE = "merEditRate";		// 修改费率
	
	final String MER_ADD_URI = "/merchant/" + MER_ADD;
	final String MER_ADD_EXT_URI = "/merchant/" + MER_ADD_EXT;
	final String MER_EDIT_URI = "/merchant/" + MER_EDIT;
	final String MER_EDIT_RATE_URI = "/merchant/" + MER_EDIT_RATE;
	
	final String TRANSFER = "transfer";
	
	final String PAY_URI = "/pay/receive";			// 支付
	
	
	private String endpoint = "http://juhe.yjlkj.cn/api";
	
	
	// 父商户号
	private String merchantNo;
	private String appKey;
	private String secret;
	
	/**
	 * 收款通知
	 */
	private String cashNotify;
	
	/**
	 * 代付通知
	 */
	private String payNotify;
	
	/**
	 * 商户通知
	 */
	private String merchantNotify;
	
	
	private AESUtils aesUtils;
	
	private List<Cashier> cashiers = new ArrayList<>();
	
	
	private CashStarter cashStarter = new CashStarter() {
		
		@Override
		public JSONObject startPay(String bizName, CashReq payReq) {
			
			JSONObject data = new JSONObject();
			data.put("orderNo", payReq.getOrderNo());
			data.put("merchantNo", payReq.getMerchantNo());
			data.put("body", payReq.getBody());
			data.put("transAmount", String.valueOf(payReq.getTransAmount()));
			String notifyUrl = payReq.getNotifyUrl();
			if (StrKit.notBlank(notifyUrl)) {
				data.put("callbackUrl", notifyUrl);
			} else {
				data.put("callbackUrl", cashNotify);
			}
			if (payReq instanceof QuickCashReq) {
				QuickCashReq quickReq = (QuickCashReq) payReq;
				data.put("channel", quickReq.getChannelCode());
				data.put("accountName", quickReq.getAccountName());
				data.put("accountNo", quickReq.getAccountNo());
				data.put("idCardNo", quickReq.getIdCardNo());
				data.put("mobileNo", quickReq.getMobileNo());
				data.put("cvn2", quickReq.getCvn2());
				data.put("expDate", quickReq.getExpDate());
				data.put("frontUrl", quickReq.getFrontUrl());
				Long maxFee = quickReq.getMaxFee();
				if (maxFee != null) {
					data.put("maxFee", maxFee);
				}
				Long t0drawFee = quickReq.getT0drawFee();
				if (t0drawFee != null) {
					data.put("t0drawFee", t0drawFee);
				}
			}
			JSONObject json = new JSONObject();
			json.put("bizName", bizName);
			json.put("data", data);
			
			try {
				String jsonString = json.toJSONString();
				log.info("支付 plain data: " + jsonString);
				String encryptData = encrypt(jsonString);
				log.info("支付 encrypt data: " + encryptData);
				
				List<NameValuePair> list = new ArrayList<>();
				list.add(new BasicNameValuePair("appKey", appKey));
				list.add(new BasicNameValuePair("data", encryptData));
				
				return post(PAY_URI, toHttpEntity(list));
				
			} catch (Exception e) {
				throw new CashierException("支付出错：", e);
			}
		}
		
	};
	
	
	private Payer payer = new Payer() {
		
		final int SUCCESS = 2;
		final int FAILED = 3;
		final int PAYING = 9;
		
		@Override
		public PayRes pay(com.ejlchina.cashier.PayReq req) {
			
			JSONObject data = new JSONObject();
			data.put("orderNo", req.getOrderNo());
			data.put("accountName", req.getAccountName());
			data.put("accountNo", req.getAccountNo());
			data.put("body", req.getMessage());
			data.put("transAmount", req.getTransAmount());
			String notifyUrl = req.getNotifyUrl();
			if (StrKit.notBlank(notifyUrl)) {
				data.put("callbackUrl", notifyUrl);
			} else {
				data.put("callbackUrl", payNotify);
			}
			JSONObject json = new JSONObject();
			json.put("bizName", TRANSFER);
			json.put("data", data);
			
			String jsonString = json.toJSONString();
			
			log.info("代付 plain data: " + jsonString);
			
			String encryptData = encrypt(jsonString);
			
			log.info("进件 encrypt data: " + encryptData);
			
			List<NameValuePair> list = new ArrayList<>();
			list.add(new BasicNameValuePair("appKey", appKey));
			list.add(new BasicNameValuePair("data", encryptData));
			
			HttpEntity entity = toHttpEntity(list);
			
			JSONObject resultJson =  post(PAY_URI, entity);
			
			PayRes res = new PayRes();
			
			JSONObject head = resultJson.getJSONObject("head");
			JSONObject content = resultJson.getJSONObject("content");
			
			if (head != null) {
				String resultCode = head.getString("result_code");
				if (Res.SUCCESS_CODE.equals(resultCode)) {
					if (content == null) {
						throw new CashierException("代付返回结果没有content");
					}
					int status = content.getInteger("status");
					if (status == SUCCESS) {
						res.setStatus(PayRes.STATUS_SUCCESS);
					} else if (status == FAILED) {
						res.setStatus(PayRes.STATUS_FAILED);
						res.setMessage(head.getString("result_msg"));
					} else if (status == PAYING) {
						res.setStatus(PayRes.STATUS_PAYING);
					} else {
						throw new CashierException("代付返回未知状态：" + status);
					}
				} else {
					res.setStatus(PayRes.STATUS_FAILED);
					res.setMessage(head.getString("result_msg"));
				}
			} else {
				throw new CashierException("返回结果没有head");
			}
			return res;
		}
	};


	public void init() {
		try {
			aesUtils = new AESUtils(secret);
		} catch (Exception e) {
			throw new CashierException("密钥构造出错：", e);
		}
		for (Cashier cashier : cashiers) {
			cashier.init(cashStarter);
		}
	}
	
	
	@Override
	public MerRes addMerchant(MerAddReq req) {

		JSONObject data = buildDataJsonFrom(req);
		data.put("account_type", "1");
		
		JSONObject json = new JSONObject();
		json.put("bizName", MER_ADD);
		json.put("data", data);

		String jsonString = json.toJSONString();
		
		log.info("进件 plain data: " + jsonString);
		
		String encryptData = encrypt(jsonString);
		
		log.info("进件 encrypt data: " + encryptData);
	
		ContentType jpegContentType = ContentType.create("image/jpeg");
		
		MultipartEntityBuilder builder = MultipartEntityBuilder.create().addTextBody("appKey", appKey).addTextBody("data", encryptData);
		
		InputStream businessLicensePic = req.getBusinessLicensePic();
		InputStream legalPersonBackPic = req.getLegalPersonBackPic();
		InputStream legalPersonFrontPic = req.getLegalPersonFrontPic();
		
		if (businessLicensePic != null) {
			builder.addBinaryBody("businessLicensePic", businessLicensePic, jpegContentType, "businessLicensePic.jpg");
		}
		if (legalPersonBackPic != null) {
			builder.addBinaryBody("legalPersonBackPic", legalPersonBackPic, jpegContentType, "legalPersonBackPic.jpg");
		}
		if (legalPersonFrontPic != null) {
			builder.addBinaryBody("legalPersonFrontPic", legalPersonFrontPic, jpegContentType, "legalPersonFrontPic.jpg");
		}
		
		JSONObject resultJson = post(MER_ADD_URI, builder.build());
	
		return resolveMerResFromResut(resultJson);
	}
	
	
	@Override
	public MerRes addMerchantExt(MerAddExtReq merAddExtReq) {

		JSONObject data = new JSONObject();
		data.put("merchant_no", merchantNo);
		data.put("merchantNo", merAddExtReq.getMerchantNo());
		data.put("cer_expdate", merAddExtReq.getCerExpdate());
		data.put("buslic_expdate", merAddExtReq.getBuslicExpdate());
		
		JSONObject json = new JSONObject();
		json.put("bizName", MER_ADD_EXT);
		json.put("data", data);
		
		String jsonString = json.toJSONString();
	
		log.info("补充进件 plain data: " + jsonString);
		
		String encryptData = encrypt(jsonString);
		
		log.info("补充进件 encrypt data: " + encryptData);

		ContentType jpegContentType = ContentType.create("image/jpeg");
		
		MultipartEntityBuilder builder = MultipartEntityBuilder.create()
			.addTextBody("appKey", appKey)
			.addTextBody("data", encryptData);

		InputStream legalPersonCardFrontPic = merAddExtReq.getLegalPersonCardFrontPic();
		InputStream legalPersonCardBackPic = merAddExtReq.getLegalPersonCardBackPic();
		InputStream legalPersonHandPic = merAddExtReq.getLegalPersonHandPic();
		InputStream storeFrontPic = merAddExtReq.getStoreFrontPic();
		
		if (legalPersonCardFrontPic != null) {
			builder.addBinaryBody("legalPersonCardFrontPic", legalPersonCardFrontPic, jpegContentType, "legalPersonCardFrontPic.jpg");
		}
		if (legalPersonCardFrontPic != null) {
			builder.addBinaryBody("legalPersonCardBackPic", legalPersonCardBackPic, jpegContentType, "legalPersonCardBackPic.jpg");
		}
		if (legalPersonHandPic != null) {
			builder.addBinaryBody("legalPersonHandPic", legalPersonHandPic, jpegContentType, "legalPersonHandPic.jpg");
		}
		if (storeFrontPic != null) {
			builder.addBinaryBody("storeFrontPic", storeFrontPic, jpegContentType, "storeFrontPic.jpg");
		}
		JSONObject resultJson = post(MER_ADD_EXT_URI, builder.build());
	
		return resolveMerResFromResut(resultJson);
	}

	@Override
	public MerRes editMerchant(MerEditReq req) {
		
		JSONObject data = buildDataJsonFrom(req);
		data.put("merchantNo", req.getMerchantNo());
		
		JSONObject json = new JSONObject();
		json.put("bizName", MER_EDIT);
		json.put("data", data);
		
		String jsonString = json.toJSONString();
		
		log.info("进件 plain data: " + jsonString);
		
		String encryptData = encrypt(jsonString);
		
		log.info("进件 encrypt data: " + encryptData);
	
		ContentType jpegContentType = ContentType.create("image/jpeg");
		
		MultipartEntityBuilder builder = MultipartEntityBuilder.create().addTextBody("appKey", appKey).addTextBody("data", encryptData);
		
		InputStream businessLicensePic = req.getBusinessLicensePic();
		InputStream legalPersonBackPic = req.getLegalPersonBackPic();
		InputStream legalPersonFrontPic = req.getLegalPersonFrontPic();
		
		if (businessLicensePic != null) {
			builder.addBinaryBody("businessLicensePic", businessLicensePic, jpegContentType, "businessLicensePic.jpg");
		}
		if (legalPersonBackPic != null) {
			builder.addBinaryBody("legalPersonBackPic", legalPersonBackPic, jpegContentType, "legalPersonBackPic.jpg");
		}
		if (legalPersonFrontPic != null) {
			builder.addBinaryBody("legalPersonFrontPic", legalPersonFrontPic, jpegContentType, "legalPersonFrontPic.jpg");
		}
		JSONObject resultJson = post(MER_EDIT_URI, builder.build());
	
		return resolveMerResFromResut(resultJson);
	}
	
	@Override
	public MerRes editMerchantRate(MerEditRateReq merEditRateReq) {
		JSONObject data = new JSONObject();
		data.put("merchantNo", merEditRateReq.getMerchantNo());
		data.put("payTypes", JSONObject.toJSONString(merEditRateReq.getPayTypes()));
		data.put("attach", merEditRateReq.getAttach());

		JSONObject json = new JSONObject();
		json.put("bizName", MER_EDIT_RATE);
		json.put("data", data);
		
		String jsonString = json.toJSONString();
		
		log.info("进件 plain data: " + jsonString);
		
		String encryptData = encrypt(jsonString);
		
		log.info("进件 encrypt data: " + encryptData);
		
		List<NameValuePair> list = new ArrayList<>();
		list.add(new BasicNameValuePair("appKey", appKey));
		list.add(new BasicNameValuePair("data", encryptData));
		
		HttpEntity entity = toHttpEntity(list);
		
		JSONObject resultJson =  post(MER_EDIT_RATE_URI, entity);
		
		return resolveMerResFromResut(resultJson);
	}

	
	@Override
	public Cashier getCashier(String bizName) {
		for (Cashier cashier : cashiers) {
			if (bizName.equals(cashier.getBizName())) {
				return cashier;
			}
		}
		throw new CashierException("没有这个Cashier【" + bizName + "】，请检查配置！");
	}

	@Override
	public Payer getPayer() {
		return payer;
	}	

	@Override
	public MerNotifyData resolveMerNotify(HttpServletRequest request) {
		try {
			String notifyData = request.getParameter("data");
			log.info("raw notify data : " + notifyData);
			String decrypt = decrypt(notifyData);
			log.info("decrypt notify data : " + decrypt);
			JSONObject json = JSON.parseObject(decrypt);
			JSONObject head = json.getJSONObject("head");
			JSONObject content = json.getJSONObject("content");
			MerNotifyData data = new MerNotifyData();
			if (head != null) {
				data.setCode(head.getString("result_code"));
				data.setMessage(head.getString("result_msg"));
			} else {
				log.error("商户通知结果没有head");
			}
			if (content != null) {
				data.setMerchantNo(content.getString("merchantNo"));
				data.setStatus(content.getIntValue("status"));
				data.setAttach(content.getString("attach"));
			} else {
				log.error("商户通知结果没有content");
			}
			return data;
		} catch (Exception e) {
			throw new CashierException("商户通知异常：", e);
		}
	}
	
	@Override
	public CashNotifyData resolveCashNotify(HttpServletRequest request) {
		try {
			String notifyData = request.getParameter("data");
			log.info("raw notify data : " + notifyData);
			String decrypt = decrypt(notifyData);
			log.info("decrypt notify data : " + decrypt);
			JSONObject json = JSON.parseObject(decrypt);
			JSONObject head = json.getJSONObject("head");
			JSONObject content = json.getJSONObject("content");
			CashNotifyData data = new CashNotifyData();
			data.setCode(head.getString("result_code"));
			data.setMessage(head.getString("result_msg"));
			data.setOrderNo(content.getString("order_no"));
			data.setStatus(content.getIntValue("status"));
			data.setType(content.getIntValue("type"));
			data.setAmount(content.getLongValue("amount"));
			return data;
		} catch (Exception e) {
			throw new CashierException("收款通知异常：", e);
		}
	}
	
	@Override
	public PayNotifyData resolvePayNotify(HttpServletRequest request) {
		try {
			String notifyData = request.getParameter("data");
			log.info("raw notify data : " + notifyData);
			String decrypt = decrypt(notifyData);
			log.info("decrypt notify data : " + decrypt);
			JSONObject json = JSON.parseObject(decrypt);
			JSONObject head = json.getJSONObject("head");
			JSONObject content = json.getJSONObject("content");
			int type = content.getIntValue("type");
			if (type == 8) {
				PayNotifyData data = new PayNotifyData();
				data.setCode(head.getString("result_code"));
				data.setMessage(head.getString("result_msg"));
				data.setOrderNo(content.getString("order_no"));
				data.setStatus(content.getIntValue("status"));
				data.setAmount(content.getLongValue("amount"));
				return data;
			} else {
				throw new CashierException("不是代付通知：type != 8");
			}
		} catch (Exception e) {
			throw new CashierException("收款通知异常：", e);
		}
	}
	

	private HttpEntity toHttpEntity(List<NameValuePair> list) {
		try {
			return new UrlEncodedFormEntity(list, "utf-8");
		} catch (UnsupportedEncodingException e) {
			throw new CashierException(e);
		}
	}
	
	/**
	 * 发送请求
	 * @param uri
	 * @param entity
	 * @return
	 * @throws Exception
	 */
	private JSONObject post(String uri, HttpEntity entity) {
		
		HttpPost post = new HttpPost(endpoint + uri) ; 
		
		post.setEntity(entity);
		
		HttpClient client = HttpClientBuilder.create().build();
		
		String result = null;
		try {
			HttpResponse response = client.execute(post);
			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode != HttpStatus.SC_OK) {
				throw new CashierException("返回状态码不是200：" + statusCode);
			}
			result = EntityUtils.toString(response.getEntity());
		} catch (Exception e) {
			throw new CashierException("请求出错：", e);
		}
		
		log.info("返回 raw result : " + result);
		
		String decrypt = decrypt(result);
		
		log.info("返回 decrypt result : " + decrypt);
		
		return JSON.parseObject(decrypt);
	}
	

	private JSONObject buildDataJsonFrom(MerAddReq req) {
		JSONObject data = new JSONObject();
		data.put("merchant_no", merchantNo);
		data.put("merchant_name", req.getMerchantName());
		data.put("merchant_short_name", req.getMerchantShortName());
		data.put("proName", req.getProvinceNo());
		data.put("cityName", req.getCityNo());
		data.put("districtName", req.getDistrictNo());
		data.put("address", req.getAddress());
		data.put("category_wx", req.getWxBusiCategoryNo());
		data.put("category_ali", req.getAliBusiCategoryNo());
		data.put("category_jd", req.getJdBusiCategoryNo());
		data.put("mchLicense", req.getBusiLicense());
		data.put("mchLicense_name", req.getBusiLicenseName());
		List<PayType> payTypes = req.getPayTypes();
		if (payTypes.size() > 0) {
			data.put("payTypes", JSONObject.toJSONString(payTypes));
		}
		data.put("bankCardNo", req.getBankCardNo());
		data.put("bankName", req.getBankNo());
		data.put("cnaps", req.getCnaps());
		data.put("bankAccName", req.getBankAccName());
		data.put("certificateNo", req.getCertificateNo());
		data.put("mobile", req.getBankMobile());
		data.put("opName", req.getOpName());
		data.put("opCertificateNo", req.getOpCertificateNo());
		data.put("opMobile", req.getOpMobile());
		data.put("opTelephone", req.getOpTelephone());
		data.put("customerTelephone", req.getCustomerTelephone());
		data.put("email", req.getEmail());
		data.put("attach", req.getAttach());
		String notifyUrl = req.getNotifyUrl();
		if (StrKit.notBlank(notifyUrl)) {
			data.put("notifyUrl", notifyUrl);
		} else {
			data.put("notifyUrl", merchantNotify);
		}
		return data;
	}

	private MerRes resolveMerResFromResut(JSONObject resultJson) {
		
		JSONObject head = resultJson.getJSONObject("head");
		JSONObject content = resultJson.getJSONObject("content");
		
		MerRes merRes = new MerRes();
		if (head != null) {
			merRes.setCode(head.getString("result_code"));
			merRes.setMessage(head.getString("result_msg"));
		} else {
			throw new CashierException("返回结果没有head");
		}
		if (content != null) {
			merRes.setMerchantNo(content.getString("merchantNo"));
			merRes.putExtInfo("status", content.getInteger("status"));
			merRes.putExtInfo("fastAuditStatus", content.getJSONArray("fastAuditStatus"));
		}
		return merRes;
	}
	
	/**
	 * 加密
	 * @param raw
	 * @return
	 */
	private String encrypt(String raw) {
		try {
			return aesUtils.encrypt(raw);
		} catch (Exception e) {
			throw new CashierException("加密出错：", e);
		}
	}
	
	/**
	 * 解密
	 * @param encrypt
	 * @return
	 */
	private String decrypt(String encrypt) {
		try {
			return aesUtils.decrypt(encrypt);
		} catch (Exception e) {
			throw new CashierException("解密出错：", e);
		}
	}
	
	public void setCashiers(List<Cashier> cashiers) {
		this.cashiers = cashiers;
	}
	
	public void addCashier(Cashier cashier) {
		this.cashiers.add(cashier);
	}

	public void setEndpoint(String endpoint) {
		this.endpoint = endpoint;
	}

	public void setAppKey(String appKey) {
		this.appKey = appKey;
	}

	public void setSecret(String secret) {
		this.secret = secret;
	}

	public void setMerchantNo(String merchantNo) {
		this.merchantNo = merchantNo;
	}

	public void setCashNotify(String cashNotify) {
		this.cashNotify = cashNotify;
	}

	public void setPayNotify(String payNotify) {
		this.payNotify = payNotify;
	}

	public void setMerchantNotify(String merchantNotify) {
		this.merchantNotify = merchantNotify;
	}
	
}
